<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 */

/**
 * Page writer
 *
 * PHP version 5
 *
 * LICENSE: This file is part of myLib.
 *
 * myLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * myLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category Writer
 * @package myLib
 * @author Diego Machado de Assis <manejoao@gmail.com>
 * @copyright 2009 Diego Machado de Assis
 * @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE Version 3
 * @see xhtmloutputter.php head.php
 */

require_once 'xhtmloutputter.php';
require_once 'head.php';

/**
 * Page writer
 *
 * This is a base class for all pages of the site. It implements varios methods
 * that should be overwritten by more specific ones of the pages classes. This
 * class extends the XHTMLOutputter class, that contains various methods that
 * implement html elements.
 *
 * @category Writer
 * @package myLib
 * @author Diego Machado de Assis <manejoao@gmail.com>
 * @copyright 2009 Diego Machado de Assis
 * @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE Version 3
 * @see XHTMLOutputter
 */
class Page extends XHTMLOutputter {

    /**
     * The args array
     *
     * Receives the page args, with the args names beeing the array keys
     *
     * @var Array
     * @access private
     */
    private $args;
    private $sess_name;

    /**
     * Constructor
     *
     * Just call the XHTMLOutputter constructor
     *
     * @param string    $output output file to the writer. Defaults to stdout
     * @param boolean   $indent wheter the output should be indented. Defaults
     *                          to TRUE
     *
     * @access public
     * @see XMLOutputter::__construct
     * @see XHTMLOutputter::__construct
     */
    public function __construct($output='php://output', $indent=true) {
        parent::__construct($output, $indent);
    }

    /**
     * Method used to initialize attributes of the class
     *
     * @param array $args   misc array of arguments
     *
     * @return boolean TRUE
     *
     * @access public
     */
    public function prepare($args) {
        $newargs = array();
        $quoted = get_magic_quotes_gpc();
        foreach($args as $key => $value) {
            $newargs[$key] = $quoted ? stripslashes($value) : $value;
        }
        $this->args = &$newargs;

        $this->current_user = false;

        return true;
    }

    /**
     * Method that handle the request
     *
     * @access public
     */
    public function handle() {
        $this->constructPage();
    }

    /**
     * Template method to construct a page
     *
     * @return nothing
     *
     * @access public
     * @see Page::constructHead(), Page::constructBody()
     */
    public function constructPage() {
        $this->XHTMLStart();
        $this->constructHead();
        $this->constructBody();
        $this->XHTMLEnd();
    }

    /**
     * Template method to construct the head section of a page
     *
     * @return nothing
     *
     * @access private
     * @see Page::constructTitle(), Page::constructBase(),
     *      Page::constructMeta(), Page::constructLink(),
     *      Page::constructScript(), Page::constructStyle(),
     *      Page::constructPage(), Page::conscructBody()
     */
    private function constructHead() {
        $this->elementStart('head');
        $this->constructTitle();
        $this->constructBase();
        $this->constructMeta();
        $this->constructLink();
        $this->constructScript();
        $this->constructStyle();
        $this->elementEnd('head');
    }

    /**
     * Template method to construct the title of a page
     *
     * @return boolean  TRUE on success or FALSE on failure or if title is not
     *                  setted
     *
     * @access private
     * @see Page::title()
     */
    private function constructTitle() {
        if($title = $this->title()) {
            return $this->element('title', null, $title);
        }
        return false;
    }

    /**
     * Returns the page title
     *
     * This method must be overloaded
     *
     * @return string   title of the page
     *
     * @access protected
     * @see Page::constructTitle()
     */
    protected function title() {
        return false;
    }

    /**
     * Template method to construct the base element of a page
     *
     * @return boolean  TRUE on success or FALSE on failure or if base address
     *                  is not setted
     *
     * @access private
     * @see Page::base()
     */
    private function constructBase() {
        if($base = $this->base()) {
            return $this->element('base', array('href' => $base));
        }
        return false;
    }

    /**
     * Returns the 'href' attribute of the page base element
     *
     * This method must be overloaded
     *
     * @return string   base address of the site
     *
     * @access protected
     * @see Page::constructBase()
     */
    protected function base() {
        return false;
    }

    /**
     * Template method to construct the meta elements of a page
     *
     * Iterates throws the array of Meta objects returned by Page::meta()
     * method. For each object, it calls the method Meta::getAttributes() that
     * returns an array of attributes.
     *
     * @return boolean  TRUE on success or FALSE on failure or if there is no
     *                  meta element to be written
     *
     * @access private
     * @see Meta::getAttributes(), Page::meta()
     */
    private function constructMeta() {
        if($meta = $this->meta()) {
            if(is_array($meta)) {
                foreach($meta as $metaObj) {
                    if(!($this->element('meta', $metaObj->getAttributes()))) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * Constructs an array of Meta objects
     *
     * This method must be overloaded
     *
     * @return array    array of Meta objects, each one representing one meta
     *                  tag that should be written in the page
     *
     * @access protected
     * @see Meta, Page::constructMeta()
     */
    protected function meta() {
        return false;
    }

    /**
     * Template method to construct the link elements of a page
     *
     * Iterates throws the array of Link objects returned by Page::link()
     * method. For each object, it calls the method Link::getAttributes() that
     * returns an array of attributes.
     *
     * @return boolean  TRUE on success or FALSE on failure or if there is no
     *                  link element to be written
     *
     * @access private
     * @see Link::getAttributes(), Page::link()
     */
    private function constructLink() {
        if($link = $this->link()) {
            if(is_array($link)) {
                foreach($link as $linkObj) {
                    if($linkObj->ie_cond) {
                        $this->raw('<!--[if ' . $linkObj->ie_cond . ']>');
                    }
                    $this->element('link', $linkObj->getAttributes());
                    if($linkObj->ie_cond) {
                        $this->raw('<![endif]-->');
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * Constructs an array of Link objects
     *
     * This method must be overloaded
     *
     * @return array    array of Link objects, each one representing one link
     *                  tag that should be written in the page
     *
     * @access protected
     * @see Link, Page::constructLink()
     */
    protected function link() {
        return false;
    }

    /**
     * Template method to construct the script elements of a page
     *
     * Iterates throws the array of Script objects returned by Page::script()
     * method. For each object, it starts a new script element calling the
     * method XMLOutputter::elementStart() with the attributes returned by the
     * method Script::getAttributes(). Then it writes the content of the script
     * element, returned by the method Script::getContent(). Finally, it ends
     * the script element with a call to XMLOutputter::elementEnd() method.
     *
     * @return boolean  TRUE on success or FALSE on failure or if there is no
     *                  script element to be written
     *
     * @access private
     * @see Script::getAttributes(), Script::getContent(), Page::script()
     */
    private function constructScript() {
        if($script = $this->script()) {
            if(is_array($script)) {
                foreach($script as $scriptObj) {
                    if($scriptObj->ie_cond) {
                        $this->raw('<!--[if ' . $scriptObj->ie_cond . ']>');
                    }
                    $this->elementStart('script', $scriptObj->getAttributes());
                    $this->cdata($scriptObj->getContent());
                    $this->elementEnd('script');
                    if($scriptObj->ie_cond) {
                        $this->raw('<![endif]-->');
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * Constructs an array of Script objects
     *
     * This method must be overloaded
     *
     * @return array    array of Script objects, each one representing one
     *                  script element that should be written in the page
     *
     * @access protected
     * @see Script, Page::constructScript()
     */
    protected function script() {
        return false;
    }

    /**
     * Template method to construct the style elements of a page
     *
     * Iterates throws the array of Style objects returned by Page::style()
     * method. For each object, it starts a new style element calling the method
     * XMLOutputter::elementStart() with the attributes returned by the method
     * Style::getAttributes(). Then it writes the content of the style element,
     * returned by the method Style::getContent(). Finally, it ends the style
     * element with a call to XMLOutputter::elementEnd() method.
     *
     * @return boolean  TRUE on success or FALSE on failure or if there is no
     *                  style element to be written
     *
     * @access private
     * @see Style::getAttributes(), Style::getContent(), Page::style()
     */
    private function constructStyle() {
        if($style = $this->style()) {
            if(is_array($style)) {
                foreach($style as $styleObj) {
                    if(!( $this->elementStart('style',
                                    $styleObj->getAttributes()) &&
                                $this->raw($styleObj->getContent()) &&
                                $this->elementEnd('style') )) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * Constructs an array of Style objects
     *
     * This method must be overloaded
     *
     * @return array    array of Style objects, each one representing one style
     *                  element that should be written in the page
     *
     * @access protected
     * @see Style, Page::constructStyle()
     */
    protected function style() {
        return false;
    }

    /**
     * Template method to construct the body section of a page
     *
     * @return nothing
     *
     * @access private
     * @see Page::body(), Page::constructPage(), Page::constructHead()
     */
    private function constructBody() {
        $this->elementStart('body');
        $this->body();
        $this->elementEnd('body');
    }

    /**
     * Constructs the page body
     *
     * This method must be overloaded
     *
     * @return nothing
     *
     * @access protected
     * @see Page::constructBody()
     */
    protected function body() {
    }

    /**
     * Returns the value of $key argument of the page
     *
     * @param string    $key        requested argument
     * @param string    $def        default value to be returned if the $key
     *                              argument is not present
     * @param boolean   $trimmed    if the value of $key argument should be
     *                              trimmed before return
     *
     * @return the value of $key argument or $def if it is not present
     *
     * @access protected
     */
    protected function getArg($key, $def = null, $trimmed = true) {
        if(array_key_exists($key, $this->args)) {
            $arg = $this->args[$key];
            return $trimmed ? trim($arg) : $arg;
        }
        return $def;
    }

    protected function setArg($key, $value) {
        if(array_key_exists($key, $this->args)) {
            $this->args[$key] = $value;
            return true;
        }
        return false;
    }

    protected function startSession($name = null) {
        if($name && is_string($name)) {
            $this->sess_name = $name;
        }
        session_name($this->sess_name);
        @session_start();

        if(!self::isRegisteredSession('started')) {
            session_regenerate_id();
            self::registerSession('started', time());
        }

    }

    protected function startLoginSession($name = null) {
        //session_cache_expire(30);
        self::startSession($name);
        @session_destroy();
        self::startSession($name);
    }

    protected function registerSession($key, $val) {
        if($this->sess_name) {
            session_name($this->sess_name);
        }
        $_SESSION[$key] = $val;
    }

    protected function isRegisteredSession($key) {
        if($this->sess_name) {
            session_name($this->sess_name);
        }
        return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
    }

    protected function haveSession() {
        return (strcmp(session_id(), '') !== 0);
    }

    protected function loggedIn() {
        $user = $this->currentUser();
        return !( is_null($user) || ($user === false) );
    }

    protected function currentUser() {
        return null;
    }

}
